package com.withouther.robot.rainingrobot.core.websocket;

import com.withouther.robot.rainingrobot.core.constants.RoleType;
import com.withouther.robot.rainingrobot.service.AudioAsrService;
import com.withouther.robot.rainingrobot.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.*;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ClassName AudioMessageWebScoket
 * @Auther: tzq
 * @Date: 2020/6/10 08:44
 * @Description:
 */
@SuppressWarnings("all")
@ServerEndpoint("/audioMessage/{uid}/{roleType}")
@Component
@Slf4j
public class AudioMessageWebScoket {

    // 用户和websocket的session映射
    public static final ConcurrentHashMap<String, Session> sessions = new ConcurrentHashMap<String, Session>();
    ;
    // session超时时间  5分钟
    private static final long MAX_TIME_OUT = 5 * 60 * 1000;

    private AudioAsrService audioAsrService;

    @OnOpen
    public void onOpen(Session session, @PathParam("uid") String uid, @PathParam("roleType") int roleType) {
        session.setMaxIdleTimeout(MAX_TIME_OUT);
        String key = WebScoketUtils.getScoketKey(uid, roleType);
        log.info("onOpen WebScoket channleId :{}", key);
        try {
            WebScoketUtils.setAsrFilePath(uid, roleType);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (roleType == RoleType.SERVICE) {
            sessions.put(key, session);
        }

        if (audioAsrService == null) {
            audioAsrService = (AudioAsrService) SpringContextUtil.getBean("audioAsrService");
        }
        Map<String, Object> parameters = new HashMap<>();
        parameters.put("sampleRate", 8000);
        parameters.put("byteLength", 2048);
        audioAsrService.startAsr(uid, roleType, parameters);
    }

    @OnClose
    public void onClose(Session session, @PathParam("uid") String uid, @PathParam("roleType") int roleType) {
        remove(session, uid, roleType);
    }

    /**
     * 1.获取到前台传输来的语音流，并且储存到asr需要识别的文件中
     * 2.WebScoketUtils.intentSwitchMap判断机器人是否可以说话
     * 3.判断公共变量中使用存有TTS语音文件，如果存在的话，就转换为byte数组传输给前台
     * @param message
     * @param session
     * @param uid
     * @param roleType
     * @throws IOException
     */
    @OnMessage(maxMessageSize = 1024000)
    public void onMessage(byte[] message, Session session, @PathParam("uid") String uid, @PathParam("roleType") int roleType) throws IOException {
        String keyId = WebScoketUtils.getScoketKey(uid, roleType);
        writeFile(message, uid, roleType);
        try {
            if (null != WebScoketUtils.userMap.get(keyId) && !WebScoketUtils.userMap.get(keyId).equals("")) {
                if ("on".equals(WebScoketUtils.intentSwitchMap.get(keyId))) {
                    log.info("机器人说话");
                    String filePath = WebScoketUtils.userMap.get(keyId);
                    WebScoketUtils.userMap.remove(keyId);
                    onMessageByte(keyId, filePath);
                    WebScoketUtils.intentSwitchMap.put(keyId, "off");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 发送语音流方法
     *
     * @param scoketKey
     * @param filePath
     * @throws IOException
     */
    public static void onMessageByte(String scoketKey, String filePath) throws Exception {
        Session session = sessions.get(scoketKey);
        if (session == null) {
            return;
        }
        // 转换为可播放的字节
        byte[] data = Util.readFileByBytes(filePath);
        // 储存语音
        WebScoketUtils.writeFile(data, WebScoketUtils.getAsrWebScoket(scoketKey).getRecordFile());
        ByteBuffer buf = ByteBuffer.wrap(data);
        session.getBasicRemote().sendBinary(buf);
        // 休眠时间
        int fileTime = VideoUtil.getFileTime(new File(filePath));
        session.getBasicRemote().sendText("stop-" + (fileTime + 1));
        log.info("播报的语音文件：" + filePath + ",时长(毫秒): " + fileTime);
    }


    @OnMessage
    public void onMessage(String message, Session session, @PathParam("uid") String uid,  @PathParam("roleType") int roleType) {
        log.info("webscoket channelId:" + uid + ";消息为： " + message);
        String keyId = WebScoketUtils.getScoketKey(uid, roleType);
        if (message.equals("on")) {
            WebScoketUtils.intentSwitchMap.put(keyId, "on");
            log.info("语音播报完毕，开启意图解析");
        }
    }

    public static void sendMessage(String scoketKey, String message) throws IOException {
        Session session = sessions.get(scoketKey);
        if (session == null) {
            return;
        }
        synchronized (session) {
            session.getBasicRemote().sendText(message);
        }
    }

    public void writeFile(byte[] bfile, String uid, int roleType) {
        String key = WebScoketUtils.getScoketKey(uid, roleType);
        FileOutputStream recodeOutput = WebScoketUtils.getAsrWebScoket(key).getFileOutputStream(); //人
        try {
            if ("off".equals(WebScoketUtils.intentSwitchMap.get(key))) {
                bfile = new byte[2048];
            }
            recodeOutput.write(bfile, 0, bfile.length);
            if ("on".equals(WebScoketUtils.intentSwitchMap.get(key))) {
                WebScoketUtils.writeFile(bfile, WebScoketUtils.getAsrWebScoket(key).getRecordFile());
            }
        } catch (Exception e) {
            log.info("文件名为：" + WebScoketUtils.webScoketMap.get(key).getFilePath());
            String sessionKey = WebScoketUtils.getScoketSessionKey(uid, roleType);
            remove(sessions.get(sessionKey), uid, roleType);
            e.printStackTrace();
        }
    }

    private void remove(Session session, String uid, int roleType) {
        log.info("webscoket close channleId:" + uid + "；角色为：" + RoleType.getRoleTypeName(roleType));
        String scoketKey = WebScoketUtils.getScoketKey(uid, roleType);
        FileOutputStream recodeOutput = WebScoketUtils.getAsrWebScoket(scoketKey) == null ? null : WebScoketUtils.getAsrWebScoket(scoketKey).getFileOutputStream();
        log.info("remove key :{}", scoketKey);
        String sessionKey = WebScoketUtils.getScoketSessionKey(uid, roleType);
        log.info("sessions key :{}", sessions);
        WebScoketUtils.removeAsrWebScoket(scoketKey);
        sessions.remove(sessionKey);
        StaticParam.routeNodeMap.remove(scoketKey);

        try {
            if (session != null && session.isOpen()) {
                session.close(); // 关闭session
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (recodeOutput != null) {
                try {
                    recodeOutput.close();
                } catch (IOException e) {

                    e.printStackTrace();
                }
            }
        }
    }


}
